#include "DXUT.h"
#include "EntityLiving.h"
#include "../World.h"

EntityLiving::EntityLiving(const D3DXVECTOR3& pos) : Entity(pos) {
	m_HP		= 100.0; m_MaxHP		= 100.0;	m_HPreg		= 0.0f;
	m_EP		= 100.0; m_MaxEP		= 100.0;	m_EPreg		= 10.0f;		
	m_Speed		= 1.0f;

	m_Swimming	= false;
	m_Living	= true;

	m_Dropheight= FLT_MIN;

	m_HPregmod	= 1.0f;
}

EntityLiving::EntityLiving(const D3DXVECTOR3& pos, const D3DXAABB& aabb) : Entity(pos, aabb) {
	m_HP		= 100.0; m_MaxHP		= 100.0;	m_HPreg		= 0.0f;
	m_EP		= 100.0; m_MaxEP		= 100.0;	m_EPreg		= 10.0f;		
	m_Speed		= 1.0f;

	m_Swimming	= false;
	m_Living	= true;

	m_Dropheight= FLT_MIN;

	m_HPregmod	= 1.0f;
}

float EntityLiving::getHP() {
	return m_HP;
}

float EntityLiving::getMaxHP() {
	return m_MaxHP;
}

float EntityLiving::getHPRatio() {
	return m_HP / m_MaxHP;
}

float EntityLiving::getHPregRatio() {
	return (m_HP + m_HPreg) / m_MaxHP;
}

float EntityLiving::getEPRatio() {
	return m_EP / m_MaxEP;
}

float EntityLiving::getMaxEP() {
	return m_MaxEP;
}

void EntityLiving::calcHP(Block block, float temp, float humid, float fElapsedTime) {
	/* --- HP --- */
	/* HEAT DMG */
	if ((temp - humid) > 1.25f)
		inflictDamage(Damage(FIRE, fElapsedTime * 1.1f));
	/* LAVA DMG */
	if (block.blockID == LAVA) {
		inflictDamage(Damage(FIRE | PENETRATING, fElapsedTime * 25.0f));
		inflictStatus(1, 5.0f);
	}

	if (m_HP <= 0.0f) {
		m_Living = false;
		m_HP	= 0.0f;
		m_HPreg = 0.0f;
	}

	if (m_HPreg > 0.0f) {
		m_HP = min(m_MaxHP, m_HP + fElapsedTime * m_HPregmod);
		m_HPreg -= fElapsedTime * m_HPregmod;
	};
	/* --- HP --- */
}


void EntityLiving::calcEP(Block block, float temp, float humid, float fElapsedTime) {
	/* --- EP --- */
	if (temp < -0.9f) {
		m_MaxEP = max(25.0f, m_MaxEP - fElapsedTime * 0.05f);
		m_EP	= min(m_MaxEP, m_EP);
	}
	m_EP = min(m_MaxEP, m_EP + m_EPreg * fElapsedTime);
	/* --- EP --- */
}

void EntityLiving::calcStatus(float fElapsedTime) {
	if (m_Burning.aktiv()) {
		inflictDamage(Damage(FIRE, fElapsedTime * 5.0f));
		 m_Burning -= fElapsedTime;
	}
	if (m_Poisoned.aktiv()) {
		inflictDamage(Damage(NATURE, fElapsedTime * 1.0f));
		m_Poisoned -= fElapsedTime;
		if (!m_Poisoned.aktiv()) {
			m_HPregmod = 1.0f;
		} else {
			m_HPregmod = 0.5f;
		}
	}
	if (m_Chilled.aktiv()) {
		m_Chilled -= fElapsedTime;
		if (!m_Chilled.aktiv()) {
			m_EPreg	  = 5.0f;
		} else {
			m_EPreg	  = 10.0f;
		}
	}
	if (m_Regeneration.aktiv()) {
		m_Regeneration -= fElapsedTime;
		if (m_HP < m_MaxHP) {
			m_HP += fElapsedTime * 2.0f * m_Regeneration.iStrength;
			if (m_HP > m_MaxHP)
				m_HP = m_MaxHP;
			if (m_HP + m_HPreg > m_MaxHP)
				m_HPreg = m_MaxHP - m_HP;
		}
	}
};

void EntityLiving::OnFrameMove( EntityWorld * pWorld, double fTime, float fElapsedTime) {
	Entity::OnFrameMove(pWorld, fTime, fElapsedTime * m_Speed);

	int bx = static_cast<int>(floor(m_Position.x + 0.5f)); 
	int by = static_cast<int>(floor(m_Position.y + 0.5f)); 
	int bz = static_cast<int>(floor(m_Position.z + 0.5f));
	Block block = pWorld->getBlock(bx, bz, by);
	float temp  = pWorld->getTemperature(bx, bz);
	float humid = pWorld->getHumidity(bx, bz);

	calcEP(block, temp, humid, fElapsedTime);
	calcHP(block, temp, humid, fElapsedTime);
	calcStatus(fElapsedTime);

	if ((block.blockID == WATER) || (block.blockID == LAVA))
		m_Swimming = true;
	else
		m_Swimming = false;

	if (m_Swimming) {
		m_Velocity.y -= fElapsedTime * 20.0f;
		m_Velocity.y = max(m_Velocity.y, -5.0f);
	} else if (!m_Grounded) { // Mid-air = falling
		m_Velocity.y -= fElapsedTime * 40.0f;
		m_Velocity.y = max(m_Velocity.y, -50.0f);
	}

	if (m_Swimming) {
		m_Dropheight = FLT_MIN;
	} else if (!m_Grounded) {
		m_Dropheight = max(m_Dropheight, m_Position.y);
	} else {
		if (m_Dropheight > m_Position.y) {
			float dist = m_Dropheight - m_Position.y;
			if (dist >  3.5f) {
				dist -= 3.0f;
				dist *= 2.0f;
				inflictDamage(Damage(FALLDAMAGE, dist));
			}
		}
		m_Dropheight = FLT_MIN;
	}
}

void  EntityLiving::inflictDamage(Damage dmg) {
	dmg /= m_Armor;

	m_HP	= max(0.0, m_HP - dmg.iValue);
	if (dmg.clearReg())
		m_HPreg = min(m_MaxHP, dmg.iValue / 2.0f);
	else 
		m_HPreg = min(m_MaxHP, m_HPreg + dmg.iValue);

	if (m_HP <= 0.0)
		m_Living = false;
}

void  EntityLiving::inflictStatus(int type, float time) {
	switch (type)
	{
	case -1:
		m_Regeneration = Status(max(m_Regeneration.fTimeLeft, time), 1);
		break;
	case 1:
		m_Burning = Status(max(m_Burning.fTimeLeft, time), 1);
		break;	
	case 2:
		m_Poisoned = Status(max(m_Poisoned.fTimeLeft, time), 1);
		break;
	case 3:
		m_Chilled = Status(max(m_Chilled.fTimeLeft, time), 1);
		break;
	default:
		break;
	}
}
